package com.df.my_net_game.net.tcp.client;

import com.df.my_net_game.game_rpc.EzGameMsg;
import com.df.my_net_game.game_rpc.GRpcHeader;
import com.df.my_net_game.net.base.EzGameMsgDispatcher;
import com.df.my_net_game.proto.equip.C2G_EquipUpLevel;
import com.df.my_net_game.proto.equip.G2C_EquipUpLevel;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.ByteToMessageCodec;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class TcpClient {
    private final String host;
    private final int port;
    private NioEventLoopGroup workerGroup;
    private Channel channel;

    public TcpClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void connect() throws InterruptedException {
        workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline()
                                    .addLast(new ClientHandler());
                        }
                    });

            ChannelFuture future = bootstrap.connect(host, port).sync();
            channel = future.channel();
            future.addListener(f -> {
                if (f.isSuccess()) {
                    System.out.println("Connected to server " + host + ":" + port);
                    EzGameMsg msg = new EzGameMsg();
                    GRpcHeader header = new GRpcHeader();
                    header.setCmdId(10001);
                    header.setMsgType(1);
                    header.setReqFrom(0);
                    header.addHeader("roleId","10086");
                    msg.setHeader(header);
                    //body
                    C2G_EquipUpLevel body = new C2G_EquipUpLevel();
                    body.setLevel(10);
                    msg.setBodyObj(body);
                    channel.writeAndFlush(msg);


                } else {
                    System.err.println("Connection failed: " + f.cause());
                }
            });

            channel.closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

    public void disconnect() {
        if (channel != null) {
            channel.close();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
    }

    private static class ClientHandler extends ByteToMessageCodec<EzGameMsg> {


        @Override
        protected void encode(ChannelHandlerContext channelHandlerContext, EzGameMsg ezGameMsg, ByteBuf byteBuf) throws Exception {
            int len = 0;
            byte[] headerBytes = ezGameMsg.getHeader().tobytes();
            len+=headerBytes.length;
            byte[] body = ezGameMsg.toBodyBytes();
            if(body!=null){
                len+=body.length;
            }
            byteBuf.writeInt(4+len);
            byteBuf.writeBytes(headerBytes);
            if(body!=null){
                byteBuf.writeBytes(body);
            }
        }

        @Override
        protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
            try {
                //TODO 参考dubbo 的协议格式
                //魔数 2字节
                //请求还是响应 (1bit) 0请求 1响应
                //
                //协议格式：
                //魔数+消息总长度+header+body
                //魔数+cmdId+消息总长度+header+body
                //TODO 魔数处理
                if(byteBuf.readableBytes()<4){
                    return;
                }
                byteBuf.markReaderIndex();
                int allMsgLen = byteBuf.readInt();
                if(byteBuf.readableBytes() < allMsgLen-4){
                    byteBuf.resetReaderIndex();
                    return;
                }
                //todo 循环读一个消息长度进行解析
                EzGameMsg msg = new EzGameMsg();

                GRpcHeader header = new GRpcHeader();
                int headerLen = byteBuf.readInt();
                msg.setHeader(header);
                byte[] headerBytes = new byte[headerLen-4];
                byteBuf.readBytes(headerBytes);
                header.readFromBytes(headerBytes);

                int bodyLen = allMsgLen-4-headerLen;
                if(bodyLen>0){
                    byte[] bodyBytes = new byte[bodyLen];
                    byteBuf.readBytes(bodyBytes);
                    msg.setBodyBytes(bodyBytes);
                    G2C_EquipUpLevel bodyObjBytes = msg.getBodyObjBytes(G2C_EquipUpLevel.class);
                    System.out.println(bodyObjBytes);
                }
                //TODO 缓存消息？
                //TODO 消息唯一id 检测
                //TODO 如果是响应则返回给请求方

                //检测消息类型，如果是请求，则分发，如果是响应，
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        TcpClient client = new TcpClient("127.0.0.1",6666);

        client.connect();
        CountDownLatch latch = new CountDownLatch(1);
        latch.await();

    }
}
